//
//  XMLLMProvider.swift
//  XMLangChain
//
//  Created by xm on 2025/11/5.
//

import Foundation

public protocol XMChatMessage: Codable {
    associatedtype Role: XMChatRole
    
    var role: Role { get set }
    
}

public protocol XMChatResponseMessage: Decodable {}

public protocol XMAppTool: Encodable {}

private protocol Payload: Encodable {}

/// LLM 调用协议，方便后续替换不同的模型服务
public protocol XMLLMProvider {
    associatedtype RequestMessage: XMChatMessage
    associatedtype ResponseMessage: XMChatResponseMessage
    associatedtype AppTool: XMAppTool
    
    var session: URLSession { get set }
    
    func send(messages: [RequestMessage], tools: [AppTool]) async throws -> ResponseMessage
    
}

extension XMLLMProvider {
    
    func send_(url: URL, method: String, headers: [String: String], body: Encodable) async throws -> ResponseMessage {
        var request = URLRequest(url: url)
        request.httpMethod = method
        
        headers.forEach { (key: String, value: String) in
            request.setValue(key, forHTTPHeaderField: value)
        }
        
        request.httpBody = try JSONEncoder().encode(body)
        
        #if DEBUG
        if let requestBody = request.httpBody,
           let prettyRequest = prettyJSONString(from: requestBody) {
            print("request:\n\(prettyRequest)")
        } else if let raw = request.httpBody.flatMap({ String(data: $0, encoding: .utf8) }) {
            print("request:\n\(raw)")
        }
        #endif
        
        do {
            let (data, response) = try await session.data(for: request)
            
            #if DEBUG
            if let prettyResponse = prettyJSONString(from: data) {
                print("\nresponse:\n\(prettyResponse)")
            } else if let raw = String(data: data, encoding: .utf8) {
                print("\nresponse:\n\(raw)")
            }
            #endif
            
            let decoded = try JSONDecoder().decode(ResponseMessage.self, from: data)
            return decoded
        } catch {
            throw XMLangChainError.network(error)
        }
        
        
    }
    
}


#if DEBUG
private func prettyJSONString(from data: Data) -> String? {
    do {
        let object = try JSONSerialization.jsonObject(with: data, options: [])
        let prettyData = try JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted])
        return prettifyUnicode(in: prettyData)
    } catch {
        return prettifyUnicode(in: data)
    }
}

private func prettifyUnicode(in data: Data) -> String? {
    guard var string = String(data: data, encoding: .utf8) else {
        return nil
    }
    string = string.replacingOccurrences(of: "\\/", with: "/")
    if let unescaped = string.applyingTransform(.init("Any-Hex/Java"), reverse: true) {
        string = unescaped
    } else {
        let mutable = NSMutableString(string: string)
        if CFStringTransform(mutable, nil, "Any-Hex/Java" as CFString, true) {
            string = mutable as String
        }
    }
    return string
}
#endif
